package com.xbn.xbnmall.ware.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.rabbitmq.client.Channel;
import com.xbn.common.to.SkuHasStockVo;
import com.xbn.common.to.mq.StockDetailTo;
import com.xbn.common.to.mq.StockLockTo;
import com.xbn.common.utils.R;
import com.xbn.common.exception.NoStockException;
import com.xbn.xbnmall.ware.entity.WareOrderTaskDetailEntity;
import com.xbn.xbnmall.ware.entity.WareOrderTaskEntity;
import com.xbn.xbnmall.ware.feign.OrderFeignService;
import com.xbn.xbnmall.ware.feign.ProductFeignService;
import com.xbn.xbnmall.ware.service.WareOrderTaskDetailService;
import com.xbn.xbnmall.ware.service.WareOrderTaskService;
import com.xbn.xbnmall.ware.vo.OrderItemVo;
import com.xbn.xbnmall.ware.vo.OrderVo;
import com.xbn.xbnmall.ware.vo.WareSkuLockVo;
import lombok.Data;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xbn.common.utils.PageUtils;
import com.xbn.common.utils.Query;

import com.xbn.xbnmall.ware.dao.WareSkuDao;
import com.xbn.xbnmall.ware.entity.WareSkuEntity;
import com.xbn.xbnmall.ware.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Autowired
    private WareSkuDao wareSkuDao;
    @Autowired
    private ProductFeignService productFeignService;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    WareOrderTaskDetailService wareOrderTaskDetailService;
    @Autowired
    WareOrderTaskService wareOrderTaskService;
    @Autowired
    OrderFeignService orderFeignService;
    /*
    * 解锁库存
    * */
    @Override
    public void unlockStock(StockLockTo to) {

            System.out.println("收到检索库存的消息");
            Long id = to.getId();//库存工作单的id
            StockDetailTo detail = to.getStockDetailTo();
            Long skuId = detail.getSkuId();
            Long detailId = detail.getId();
   /*     查询数据库关于这个订单的锁库存消息
        如果工作单都没有就说明库存本身就锁定失败了
        （原先下单的时候库存服务出错回滚了其实库存并没有锁定）
        这种情况下无需解锁
        */
            WareOrderTaskDetailEntity detailEntity = wareOrderTaskDetailService.getById(detailId);
        /*    如果有锁定库存信息，说明库存服务没有出问题
        * 但是不排除订单服务有没有出现问题，如果去查询这个锁定库存的订单相关信息发现
         没有订单（说明订单服务出现问题了，用户根本没有下单成功），就必须解锁
         如果存在订单，我们还要去查看订单状态，如果订单状态是已取消，我们就应该去解锁库存，
         如果没有取消（其它状态），我们就 不能去解锁库存
        * */
            if(detailEntity!=null)
            {
                //查询该锁单关联的订单信息
                Long taskId = to.getId();
                WareOrderTaskEntity wareOrderTaskEntity = wareOrderTaskService.getById(taskId);
                //获取订单号
                String orderSn = wareOrderTaskEntity.getOrderSn();
                //查询订单状态
                R orderStatus = orderFeignService.getOrderStatus(orderSn);

                if(orderStatus.getCode()==0)
                {
                    //订单数据返回成功
                    OrderVo order = orderStatus.getData("data", new TypeReference<OrderVo>() {
                    });

                    if(order==null||order.getStatus()==4)
                    {  //订单被取消或者订单不存在 ，这个时候都需要解锁库存
                        //当前库存单状态是未解锁（并未真实扣减以及已解锁）的时候才可以解锁
                        if(detailEntity.getLockStatus()==1) {
                            unLockStock(detail.getSkuId(), detail.getWareId()
                                    , detail.getSkuNum(), detailId);
                        }
                        //解锁成功之后回复MQ
           /*             channel.basicAck(message.getMessageProperties().getDeliveryTag(),
                                false);*/
                    }
                }
                else {
                    throw new RuntimeException("解锁服务，订单数据返回失败");
                    //解锁失败之后回复MQ,拒接此消息，并让MQ重新发回队列，下次仍然会消费它
                    //只有成功才能删除消息，否则库存数据就会不正确
               /*     channel.basicReject(message.getMessageProperties().getDeliveryTag(),
                            true);*/
                }
            }
            else {
                //无需解锁,也回复下MQ，说明当前处理了这个消息，转交至外部方法统一处理
            /*    channel.basicAck(message.getMessageProperties().getDeliveryTag(),
                        false);*/
            }



    }
    /*
     * 解锁库存
     * */
    private void unLockStock(Long skuId,Long wareId,Integer num,Long taskDetailId)
    {
        //库存解锁
        wareSkuDao.unLockStock(skuId,wareId,num);
        //更新库存工作单状态为已解锁
        WareOrderTaskDetailEntity detailEntity = new WareOrderTaskDetailEntity();
        detailEntity.setId(taskDetailId);
        detailEntity.setLockStatus(2);//变为已解锁状态
        wareOrderTaskDetailService.updateById(detailEntity);
    }

    /*
     * 为某个订单锁定库存
     * 只有所有的商品锁定库存成功了，才视为成功，否则视为全部失败
     * */
    @Transactional
    @Override
    public Boolean orderLockStock(WareSkuLockVo vo) {
        //先保存库存工作单，以便用来追溯
        WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity();
        wareOrderTaskEntity.setOrderSn(vo.getOrderSn());
        wareOrderTaskService.save(wareOrderTaskEntity);



        List<OrderItemVo> locks = vo.getLocks();
        //查询库存
        List<SkuWareHasStock> collect = locks.stream().map(item -> {
            SkuWareHasStock stock = new SkuWareHasStock();
            Long skuId = item.getSkuId();
            stock.setSkuId(skuId);
            //找到该商品在所有仓库中库存足够的仓库id
            List<Long> wareIds= wareSkuDao.listWareIdHasSkuStock(skuId);
            stock.setWareId(wareIds);
            stock.setNum(item.getCount());
            return stock;
        }).collect(Collectors.toList());

        for (SkuWareHasStock hasStock : collect) {
            Long skuId = hasStock.getSkuId();
            List<Long> wareIds = hasStock.getWareId();
            if(wareIds==null&&wareIds.size()==0)
            {//没有任何仓库有这个商品的库存
                throw new NoStockException(skuId);
            }
            Boolean skuStocked=false;
            //去每一个仓库
            for (Long wareId : wareIds) {
                skuStocked=false;
                Long count=wareSkuDao.lockSkuStock(skuId,wareId,hasStock.getNum());
                if(count==1)
                {
                    skuStocked = true;
                    //通知MQ库存锁定成功
                    //这里要考虑万一有个单子锁库存失败导致了回滚，但是前面成功的信息都已经发给MQ了
                    //但是由于数据库Task回滚了，MQ的消息被消费的时候也无法查的纪录，所以也没有关系
                    WareOrderTaskDetailEntity wareOrderTaskDetailEntity =
                            new WareOrderTaskDetailEntity(null,skuId,"",hasStock.getNum()
                            ,wareOrderTaskEntity.getId(),wareId,1);
                    wareOrderTaskDetailService.save(wareOrderTaskDetailEntity);
                    //发送消息通知mq
                    StockLockTo stockLockTo=new StockLockTo();
                    stockLockTo.setId(wareOrderTaskEntity.getId());
                    StockDetailTo stockDetailTo=new StockDetailTo();
                    BeanUtils.copyProperties(wareOrderTaskDetailEntity,stockDetailTo);
                    stockLockTo.setStockDetailTo(stockDetailTo);
                    rabbitTemplate.convertAndSend("stock-event-exchange",
                            "stock.locked",stockLockTo);
                    break;
                }
                else {
                    //当前仓库锁库存失败，去尝试下一个仓库
                }
            }
            if(skuStocked==false)
            {
                throw new NoStockException(skuId);
            }
        }



        return true;
    }




    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        QueryWrapper<WareSkuEntity> wrapper = new QueryWrapper<>();
        String skuId= (String) params.get("skuId");
        String wareId= (String) params.get("wareId");
        if(!StringUtils.isEmpty(skuId))
        {
            wrapper.eq("sku_id",skuId);
        }
        if(!StringUtils.isEmpty(wareId))
        {
            wrapper.eq("ware_id",wareId);
        }

        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                wrapper
        );

        return new PageUtils(page);
    }

    @Override
    public void addStock(Long skuId, Long wareId, Integer skuNum) {
        //判断仓库有无该商品sku的库存信息，有就修改库存，没有就添加纪录
        List<WareSkuEntity> wareSkuEntities = wareSkuDao.selectList(new QueryWrapper<WareSkuEntity>()
                .eq("sku_id", skuId).eq("ware_id", wareId));

        if(wareSkuEntities==null||wareSkuEntities.size()==0)
        {
            WareSkuEntity wareSkuEntity = new WareSkuEntity();
            wareSkuEntity.setSkuId(skuId);
            wareSkuEntity.setStock(skuNum);
            wareSkuEntity.setWareId(wareId);
            wareSkuEntity.setStockLocked(0);
            // 远程调用获取sku的商品名称,如果失败，整个事务不回滚
            try {
                R info = productFeignService.info(skuId);
                if(info
                        .getCode()==0)
                {
                    Map<String,Object> data = (Map<String, Object>) info.get("skuInfo");

                    wareSkuEntity.setSkuName((String) data.get("skuName"));
                }
            }catch (Exception e)
            {

            }


            wareSkuDao.insert(wareSkuEntity);
        }else {
            wareSkuDao.addStock(skuId,wareId,skuNum);
        }

    }

    @Override
    public List<SkuHasStockVo> getSkusHasStock(List<Long> skuIds) {
        List<WareSkuEntity> wareSkuEntities = this.baseMapper.selectBatchSkuIds(skuIds);
        Map<Long,Integer> skuWareMap=new HashMap<>();
        skuWareMap = wareSkuEntities.stream().collect(Collectors.toMap
                (WareSkuEntity::getSkuId,
                item -> item.getStock()-item.getStockLocked()
        ));
        Map<Long, Integer> finalSkuWareMap = skuWareMap;
        List<SkuHasStockVo> collect = skuIds.stream()
                .map(skuId -> {
            SkuHasStockVo skuHasStockVo = new SkuHasStockVo();
                    //总库存量要-锁定的库存量 >0表示有库存
                    if(finalSkuWareMap.get(skuId)==null)
                    {
                        skuHasStockVo.setHasStock(false);
                        skuHasStockVo.setSkuId(skuId);
                    }
                    else {
                        skuHasStockVo.setHasStock(finalSkuWareMap.get(skuId)
                        >0);
                        skuHasStockVo.setSkuId(skuId);
                    }

            return skuHasStockVo;
        }).collect(Collectors.toList());
        return collect;
    }


    @Data
    class SkuWareHasStock{
        private Long skuId;
        private Integer num;//锁定商品的件数
        private List<Long> wareId;
    }

}